import numpy as np
import matplotlib.pyplot as plt
import math
import random
#Code for figure
#create function
np.random.seed(5)
la= 200
ss= 5
A = np.random.normal(size=(la, ss))
label = np.reshape(np.array([random.randint(0,1) for t in range(la)]),[la,1])

def f(x1):
    return sum([-label[j]*np.dot(A[j],x1)+math.log(1+math.exp(np.dot(A[j],x1))) for j in range(la)])
#原函数，输入n维list，输出1维list

def grad(x1):
    D = [sum([-label[j]*A[j,i]+A[j,i]/(1+math.exp(-np.dot(A[j],x1))) for j in range(la)]) for i in range(ss)]
    D = np.array(D)
    return np.reshape(D,[ss,1])
#梯度，输入n维list,输出n维list

def compare(x1,x2):
    return x1 if f(x1)<=f(x2) else x2
#比较函数，输入两个n维list,输出一个n维list
def GNSA(origin,epochs,rate,p):
    x = np.array(origin)
    y = np.array(origin)
    z = np.array(origin)
    dot_set = [x]
    for i in range(epochs - 1):
        y = (1 - co(p, i)) * x + co(p, i) * z
        x = compare(y - rate * grad(y),x- rate*grad(x))
        z = z - (rate / (co(p, i))) * grad(y)
        dot_set.append(x)
    return dot_set
#GNSA，输出epochs*n 维列表
def GD(origin,epochs,rate):
    x= np.array(origin)
    dot_set = [x]
    for i in range(epochs-1):
        x=x-rate*grad(x)
        dot_set.append(x)
    return dot_set
#梯度下降，输入初值，步数，学习率，输出epochs*n 维列表

def co(p,t):
    return p/(t+p)
def NSA(origin,epochs,rate,p):
    x= np.array(origin)
    y= np.array(origin)
    z= np.array(origin)
    dot_set = [x]
    for i in range(epochs-1):
        z=y
        y=x-np.dot(rate,grad(x))
        x=y+(1-co(p,i))*(y-z)
        dot_set.append(x)
    return dot_set
#Nesterov梯度下降，输出epochs*n 维列表

def pr_track(y):
    return
def pr_fun_value(y,min):
    plt.semilogy([i for i in range(len(y))],[f(y[i])-min for i in range(len(y))])
    return
#绘制轨迹图与函数值下降图
#调试
end=100
cend=300
or1=np.reshape(np.random.normal(size=(ss,1)),[ss,1])
e1=0.005
#print(grad_descent(origin=[-2,3,5],epochs=end,rate=0.01)[end-1])#梯度下降调试
#print(NSA(origin=[-2,3,5],epochs=end,rate=0.01,p=3)[end-1])#Nesterov梯度下降测试
#print(GNSA(origin=[-2,3,5],epochs=end,rate=0.01,p=3)[end-1])
cp = GNSA(origin=or1,epochs=cend,rate=e1,p=3)
min1 = np.min([f(cp[i]) for i in range(cend) ])
pr_fun_value(GD(origin=or1,epochs=end,rate=e1),min=min1)
pr_fun_value(NSA(origin=or1,epochs=end,rate=e1,p=3),min=min1)
pr_fun_value(NSA(origin=or1,epochs=end,rate=e1,p=4),min=min1)
pr_fun_value(GNSA(origin=or1,epochs=end,rate=e1,p=3),min=min1)
pr_fun_value(GNSA(origin=or1,epochs=end,rate=e1,p=4),min=min1)
plt.legend(loc='best',frameon=True, labels=['GD','NAG(p=3)','NAG(p=4)','NSA(p=3)','NSA(p=4)'],fontsize='large')
plt.xlabel('iteration')
plt.ylabel('f - f*')
plt.show()